# coding: utf-8


import json
import os
import random
import re
import sys
import time
import xml.dom.minidom
from pickle import load, dump
from pipes import quote
from urllib import urlencode, quote_plus
from urlparse import parse_qs

import requests

UNKONWN = 'unkonwn'
SUCCESS = '200'
SCANED = '201'
TIMEOUT = '408'
TMP_PATH = os.path.join(os.getcwd(), 'temp')

if not os.path.exists(TMP_PATH):
    os.makedirs(TMP_PATH)

PICKLE_PATH = os.path.join(TMP_PATH, 'requestpickle.data')


def show_image(file_path):
    """
    跨平台显示图片文件
    :param file_path: 图片文件路径
    """

    if sys.platform == "darwin":
        command = "open -a /Applications/Preview.app %s&" % quote(file_path)
        os.system(command)
    else:
        pass


class WXBot:
    """WXBot功能类"""

    def __init__(self):
        self.uuid = ''
        self.base_uri = ''
        self.base_host = ''
        self.redirect_uri = ''
        self.uin = ''
        self.sid = ''
        self.skey = ''
        self.pass_ticket = ''
        self.device_id = "e802709360800682"
        self.base_request = {}
        self.sync_key_str = ''
        self.sync_key = []
        self.sync_host = ''

        # 文件缓存目录
        self.temp_pwd = TMP_PATH

        self.session = requests.session()
        self.session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3150.1 Iron Safari/537.36'})

        self.my_account = {}  # 当前账户
        self.export_key = ""
        self.error_count = 0

    def proc_msg(self):
        """
        if retcode == '1100':  # 从微信客户端上登出
            break
        elif retcode == '1101':  # 从其它设备上登了网页微信
            break
        :return:
        """
        if not self.test_sync_check():
            return
        self.status = 'loginsuccess'  # WxbotManage使用
        while True:
            check_time = time.time()
            [retcode, selector] = self.sync_check()
            if retcode == '0':
                r = self.sync()
                self.get_pay()
            else:
                print '[DEBUG] sync_check:', retcode, selector
                time.sleep(10)
                break
            check_time = time.time() - check_time
            time.sleep(max(3 - check_time, 3))
        return True

    def get_pay(self):
        """
        https://wx.tenpay.com/userroll/userrolllist?count=20&sort_type=1&exportkey=A7HC9al%2FE%2FSuC5AwlrP%2FHT0%3D
        :return:
        """
        if not self.export_key:
            url = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxcheckurl?requrl=https%3A%2F%2Fwx.tenpay.com%2Fuserroll%2Freadtemplate%3Ft%3Duserroll%2Findex_tmpl&amp;skey={skey}&amp;deviceid={deviceid}&amp;pass_ticket=undefined&amp;opcode=2&amp;scene=1&amp;username=@e299aa77c9e798fc426781ffe61873d1a78ebb69d6574eb4c70daf018f590c37"
            url = url.format(skey=quote_plus(self.skey), deviceid=quote_plus(self.device_id))
            res = self.session.get(url)
            query_dict = parse_qs(res.url)
            export_key = query_dict.get('exportkey')
            if not export_key:
                return
            self.session.get(res.url)
            with open(PICKLE_PATH, 'wb') as f:
                dump(self, f)
            self.export_key = export_key[0]
        else:
            tenpay_url = "https://wx.tenpay.com/userroll/userrolllist?count=50&sort_type=1&exportkey=%s" % quote_plus(
                self.export_key)
            res = self.session.get(tenpay_url)
            res_data = res.json()
            ret_code = res_data.get("res_data")
            if ret_code == 0:
                record = res_data.get('record')
                with open('./wechat.log', 'a') as f:
                    for item in record:
                        f.write(json.dumps(item))
                        f.write('\n')
                return True
            self.error_count += 1
            if self.error_count > 5:
                return False



    def run(self):
        self.get_uuid()
        self.gen_qr_code(os.path.join(self.temp_pwd, 'wxqr.png'))
        result = self.wait4login()
        if result != SUCCESS:
            print '[ERROR] Web WeChat login failed. failed code=%s' % (result,)
            return

        if self.login():
            print '[INFO] Web WeChat login succeed .'
        else:
            print '[ERROR] Web WeChat login failed .'
            return

        if self.init():
            print '[INFO] Web WeChat init succeed .'
        else:
            print '[INFO] Web WeChat init failed'
            return
        self.status_notify()
        self.proc_msg()

    def get_uuid(self):
        url = 'https://login.weixin.qq.com/jslogin'
        params = {
            'appid': 'wx782c26e4c19acffb',
            'fun': 'new',
            'lang': 'zh_CN',
            '_': int(time.time()) * 1000 + random.randint(1, 999),
        }
        r = self.session.get(url, params=params)
        r.encoding = 'utf-8'
        data = r.text
        regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"'
        pm = re.search(regx, data)
        if pm:
            code = pm.group(1)
            self.uuid = pm.group(2)
            return code == '200'
        return False

    def gen_qr_code(self, qr_file_path):
        url = 'https://login.weixin.qq.com/qrcode/' + self.uuid
        res = self.session.get(url)
        with open(qr_file_path, 'wb') as f:
            f.write(res.content)
        show_image(qr_file_path)

    def do_request(self, url):
        r = self.session.get(url)
        r.encoding = 'utf-8'
        data = r.text
        param = re.search(r'window.code=(\d+);', data)
        code = param.group(1)
        return code, data

    def wait4login(self):
        """
        http comet:
        tip=1, 等待用户扫描二维码,
               201: scaned
               408: timeout
        tip=0, 等待用户确认登录,
               200: confirmed
        https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=wdWUZmge8w==&tip=0&r=1458876541&_=1506074384439
        """
        print '[INFO] Please use WeChat to scan the QR code .'
        LOGIN_TEMPLATE = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s'
        tip = 1

        try_later_secs = 2
        MAX_RETRY_TIMES = 10

        code = UNKONWN

        retry_time = MAX_RETRY_TIMES
        while retry_time > 0:
            url = LOGIN_TEMPLATE % (tip, self.uuid, int(time.time()))
            code, data = self.do_request(url)
            if code == SCANED:
                print '[INFO] Please confirm to login .'
                tip = 0
            elif code == SUCCESS:  # 确认登录成功
                param = re.search(r'window.redirect_uri="(\S+?)";', data)
                redirect_uri = param.group(1) + '&fun=new'
                self.redirect_uri = redirect_uri
                self.base_uri = redirect_uri[:redirect_uri.rfind('/')]
                temp_host = self.base_uri[8:]
                self.base_host = temp_host[:temp_host.find("/")]
                return code
            elif code == TIMEOUT:
                print '[ERROR] WeChat login timeout. retry in %s secs later...' % (try_later_secs,)

                tip = 1  # 重置
                retry_time -= 1
                time.sleep(try_later_secs)
            else:
                print ('[ERROR] WeChat login exception return_code=%s. retry in %s secs later...' %
                       (code, try_later_secs))
                tip = 1
                retry_time -= 1
                time.sleep(try_later_secs)

        return code

    def login(self):
        if len(self.redirect_uri) < 4:
            print '[ERROR] Login failed due to network problem, please try again.'
            return False
        r = self.session.get(self.redirect_uri)
        r.encoding = 'utf-8'
        data = r.text
        doc = xml.dom.minidom.parseString(data)
        root = doc.documentElement

        for node in root.childNodes:
            if node.nodeName == 'skey':
                self.skey = node.childNodes[0].data
            elif node.nodeName == 'wxsid':
                self.sid = node.childNodes[0].data
            elif node.nodeName == 'wxuin':
                self.uin = node.childNodes[0].data
            elif node.nodeName == 'pass_ticket':
                self.pass_ticket = node.childNodes[0].data

        if '' in (self.skey, self.sid, self.uin, self.pass_ticket):
            return False

        self.base_request = {
            'Uin': self.uin,
            'Sid': self.sid,
            'Skey': self.skey,
            'DeviceID': self.device_id,
        }
        return True

    def init(self):
        url = self.base_uri + '/webwxinit?r=%i&lang=en_US&pass_ticket=%s' % (int(time.time()), self.pass_ticket)
        params = {
            'BaseRequest': self.base_request
        }
        r = self.session.post(url, data=json.dumps(params))
        dic = r.json()
        self.sync_key = dic['SyncKey']
        self.my_account = dic['User']
        self.sync_key_str = '|'.join([str(keyVal['Key']) + '_' + str(keyVal['Val'])
                                      for keyVal in self.sync_key['List']])
        return dic['BaseResponse']['Ret'] == 0

    def status_notify(self):
        url = self.base_uri + '/webwxstatusnotify?lang=zh_CN&pass_ticket=%s' % self.pass_ticket
        self.base_request['Uin'] = int(self.base_request['Uin'])
        params = {
            'BaseRequest': self.base_request,
            "Code": 3,
            "FromUserName": self.my_account['UserName'],
            "ToUserName": self.my_account['UserName'],
            "ClientMsgId": int(time.time())
        }
        r = self.session.post(url, data=json.dumps(params))
        r.encoding = 'utf-8'
        dic = json.loads(r.text)
        return dic['BaseResponse']['Ret'] == 0

    def test_sync_check(self):
        for host1 in ['webpush.', 'webpush2.']:
            self.sync_host = host1 + self.base_host
            try:
                retcode = self.sync_check()[0]
            except:
                retcode = -1
            if retcode == '0':
                return True
        return False

    def sync_check(self):
        params = {
            'r': int(time.time()),
            'sid': self.sid,
            'uin': self.uin,
            'skey': self.skey,
            'deviceid': self.device_id,
            'synckey': self.sync_key_str,
            '_': int(time.time()),
        }
        url = 'https://' + self.sync_host + '/cgi-bin/mmwebwx-bin/synccheck?' + urlencode(params)
        r = self.session.get(url, timeout=60)
        r.encoding = 'utf-8'
        data = r.text
        pm = re.search(r'window.synccheck=\{retcode:"(\d+)",selector:"(\d+)"\}', data)
        retcode = pm.group(1)
        selector = pm.group(2)
        return [retcode, selector]

    def sync(self):
        url = self.base_uri + '/webwxsync?sid=%s&skey=%s&lang=en_US&pass_ticket=%s' \
                              % (self.sid, self.skey, self.pass_ticket)
        params = {
            'BaseRequest': self.base_request,
            'SyncKey': self.sync_key,
            'rr': ~int(time.time())
        }
        try:
            r = self.session.post(url, data=json.dumps(params), timeout=60)
            dic = r.json()
            if dic['BaseResponse']['Ret'] == 0:
                self.sync_key = dic['SyncCheckKey']
                self.sync_key_str = '|'.join([str(keyVal['Key']) + '_' + str(keyVal['Val'])
                                              for keyVal in self.sync_key['List']])
            return dic
        except:
            return None


if __name__ == '__main__':
    if os.path.exists(PICKLE_PATH):
        with open(PICKLE_PATH, 'r') as f:
            bot = load(f)
        bot.export_key = ""
    else:
        bot = WXBot()
    process = bot.proc_msg()
    if not process:
        bot.run()
